﻿//******************************************************************************************************
//  PdcTestBench.cs - Gbtc
//
//  Copyright © 2010, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
//  not use this file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://www.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  07/08/2010 - Stephen C. Wills
//       Generated original version of source code.
//
//******************************************************************************************************

using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
using TestBenchApi;
using TestBenchApi.Package;
using TVA;
using TVA.Communication;

namespace PdcTestBench
{
    /// <summary>
    /// The main form of the PDC Test Bench application.
    /// </summary>
    public partial class PdcTestBench : Form
    {

        #region [ Members ]

        // Fields

        private string m_packageFilePath;
        private PackageFile m_packageFile;
        private TestBench m_testBench;
        private SampleFileCleaner m_sampleFileCleaner;
        private bool m_inputFileChanged;
        private bool m_unsavedChanges;

        #endregion

        #region [ Constructors ]

        /// <summary>
        /// Creates a new instance of the <see cref="PdcTestBench"/> class.
        /// </summary>
        public PdcTestBench()
        {
            InitializeComponent();
            ProcessArguments(Environment.GetCommandLineArgs());

            m_packageFile = new PackageFile();
            m_testBench = new TestBench();
            m_testBench.DisplayErrorMessage += m_testBench_DisplayErrorMessage;
            m_testBench.TestBenchStarted += m_testBench_TestBenchStarted;
            m_testBench.TestBenchStopped += m_testBench_TestBenchStopped;
            m_sampleFileCleaner = SampleFileCleaner.Clean(generatorInputDataFileTextBox.Text);
        }

        #endregion

        #region [ Properties ]

        /// <summary>
        /// Gets the text shown on the form's title bar.
        /// </summary>
        public string Title
        {
            get
            {
                string title;

                if (m_packageFilePath == null)
                    title = "Untitled.tbp";
                else
                    title = m_packageFilePath;

                if (m_unsavedChanges)
                    title += '*';

                return title;
            }
        }

        /// <summary>
        /// Gets or sets the path of the currently opened package file.
        /// </summary>
        public string PackageFilePath
        {
            get
            {
                return m_packageFilePath;
            }
            set
            {
                m_packageFilePath = value;
                this.Text = Title;
            }
        }

        /// <summary>
        /// Gets the currently opened package file.
        /// </summary>
        public PackageFile PackageFile
        {
            get
            {
                return m_packageFile;
            }
        }

        /// <summary>
        /// Gets or sets whether there are unsaved changes to the package file.
        /// </summary>
        public bool UnsavedChanges
        {
            get
            {
                return m_unsavedChanges;
            }
            set
            {
                m_unsavedChanges = value;
                this.Text = Title;
            }
        }

        #endregion

        #region [ Methods ]

        // Processes command line arguments.
        private void ProcessArguments(string[] args)
        {
            if (args.Length > 1)
            {
                string firstArg = args[1];

                if (firstArg.EndsWith(".tbp"))
                    OpenPackageFile(firstArg);
            }
        }

        // Open a package file with a file path specified by the user.
        private void OpenPackageFile()
        {
            DialogResult result = packageOpenFileDialog.ShowDialog();

            if (result == DialogResult.OK)
                OpenPackageFile(packageOpenFileDialog.FileName);
        }

        // Open a package file with the given file path.
        private void OpenPackageFile(string filePath)
        {
            m_packageFile = PackageFile.Load(filePath);

            GeneratorGroupEntry generatorGroup = m_packageFile.GeneratorGroup;
            ConcentratorEntry concentrator = m_packageFile.Concentrator;
            ReceiverEntry receiver = m_packageFile.Receiver;

            // Populate generators tab.
            generatorInputDataFileTextBox.Text = generatorGroup.InputFile;
            generatorConfigurationFrameFileTextBox.Text = generatorGroup.ConfigurationFrameFile;
            generatorFrameRateTextBox.Text = generatorGroup.FrameRate.ToString();
            generatorTcpRadioButton.Checked = generatorGroup.OutputProtocol == TransportProtocol.Tcp;
            generatorUdpRadioButton.Checked = generatorGroup.OutputProtocol == TransportProtocol.Udp;
            generatorAccessIdTextBox.Text = generatorGroup.AccessId.ToString();
            pmuCountNumericUpDown.Value = generatorGroup.NumberOfGenerators;
            generatorStartPortTextBox.Text = generatorGroup.StartPort.ToString();

            // Populate concentrator tab.
            concentratorMakeTextBox.Text = concentrator.Make;
            concentratorModelTextBox.Text = concentrator.Model;
            concentratorFirmwareTextBox.Text = concentrator.Firmware;
            concentratorHostNameTextBox.Text = concentrator.HostName;
            concentratorAccessIdTextBox.Text = concentrator.AccessId.ToString();

            // Populate receiver tab.
            receiverTcpRadioButton.Checked = receiver.InputProtocol == TransportProtocol.Tcp;
            receiverUdpRadioButton.Checked = receiver.InputProtocol == TransportProtocol.Udp;
            receiverPortTextBox.Text = receiver.Port.ToString();
            receiverOutputFileTextBox.Text = receiver.OutputFile;
            receiverTimestampFileTextBox.Text = receiver.TimestampFile;
            receiverCommandChannelPortTextBox.Text = receiver.CommandChannelPort.ToString();

            PackageFilePath = filePath;
            UnsavedChanges = false;
        }

        // Cleans the sample file, making sure it consists only of data frames.
        private void CleanSampleFile()
        {
            try
            {
                SampleFileCleaner cleaner = SampleFileCleaner.Clean(generatorInputDataFileTextBox.Text);

                if (!cleaner.AlreadyClean)
                {
                    StringBuilder displayMessage = new StringBuilder("The sample file contains frames that are not data frames.");

                    generatorInputDataFileTextBox.TextChanged -= generatorInputDataFileTextBox_TextChanged;
                    generatorInputDataFileTextBox.Text = cleaner.DataFilePath;
                    generatorInputDataFileTextBox.TextChanged += generatorInputDataFileTextBox_TextChanged;

                    displayMessage.AppendLine();
                    displayMessage.AppendLine();
                    displayMessage.Append("The data frames have been extracted to:");
                    displayMessage.AppendLine();
                    displayMessage.Append(cleaner.DataFilePath);

                    generatorConfigurationFrameFileTextBox.Text = cleaner.ConfigFilePath;
                    if (cleaner.ConfigFilePath != null)
                    {
                        displayMessage.AppendLine();
                        displayMessage.AppendLine();
                        displayMessage.Append("The configuration frame has been extracted to:");
                        displayMessage.AppendLine();
                        displayMessage.Append(cleaner.ConfigFilePath);
                    }

                    m_inputFileChanged = false;
                    MessageBox.Show(displayMessage.ToString());
                }
            }
            catch (FileNotFoundException)
            {
                // Ignore this exception because the file name
                // may not have been fully entered by the user.
            }
        }

        // Save a package file with a file path specified by the user.
        private void SavePackageFile()
        {
            DialogResult result = packageSaveFileDialog.ShowDialog();

            if (result == DialogResult.OK)
                SavePackageFile(packageSaveFileDialog.FileName);
        }

        // Save a package file with the given file path.
        private void SavePackageFile(string filePath)
        {
            GeneratorGroupEntry generatorGroup = m_packageFile.GeneratorGroup;
            ConcentratorEntry concentrator = m_packageFile.Concentrator;
            ReceiverEntry receiver = m_packageFile.Receiver;

            // Populate generator group entry.
            generatorGroup.InputFile = Path.GetFullPath(generatorInputDataFileTextBox.Text);
            generatorGroup.ConfigurationFrameFile = Path.GetFullPath(generatorConfigurationFrameFileTextBox.Text);
            generatorGroup.FrameRate = int.Parse(generatorFrameRateTextBox.Text);
            generatorGroup.OutputProtocol = generatorTcpRadioButton.Checked ? TransportProtocol.Tcp : TransportProtocol.Udp;
            generatorGroup.AccessId = ushort.Parse(generatorAccessIdTextBox.Text);
            generatorGroup.NumberOfGenerators = (int)pmuCountNumericUpDown.Value;
            generatorGroup.StartPort = int.Parse(generatorStartPortTextBox.Text);

            // Populate concentrator entry.
            concentrator.Make = concentratorMakeTextBox.Text;
            concentrator.Model = concentratorModelTextBox.Text;
            concentrator.Firmware = concentratorFirmwareTextBox.Text;
            concentrator.HostName = concentratorHostNameTextBox.Text;
            concentrator.AccessId = ushort.Parse(concentratorAccessIdTextBox.Text);

            // Populate receiver entry.
            receiver.InputProtocol = receiverTcpRadioButton.Checked ? TransportProtocol.Tcp : TransportProtocol.Udp;
            receiver.Port = int.Parse(receiverPortTextBox.Text);
            receiver.OutputFile = Path.GetFullPath(receiverOutputFileTextBox.Text);
            receiver.TimestampFile = Path.GetFullPath(receiverTimestampFileTextBox.Text);

            if (receiver.InputProtocol == TransportProtocol.Tcp)
                receiver.CommandChannelPort = receiver.Port;
            else
                receiver.CommandChannelPort = int.Parse(receiverCommandChannelPortTextBox.Text);

            m_packageFile.Save(filePath);
            PackageFilePath = filePath;
            UnsavedChanges = false;
        }

        // Called when the form is closing.
        private void PdcTestBench_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (m_unsavedChanges)
            {
                DialogResult result = MessageBox.Show("You've made changes to your package. Would you like to save them?", "Save changes?", MessageBoxButtons.YesNoCancel);

                if (result == DialogResult.Cancel)
                    e.Cancel = true;
                else if (result == DialogResult.Yes)
                {
                    if (m_packageFilePath == null)
                        SavePackageFile();
                    else
                        SavePackageFile(m_packageFilePath);

                    if (m_unsavedChanges)
                        e.Cancel = true;
                }
            }
        }

        // Called when the menu item labeled "Open..." is selected.
        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenPackageFile();
        }

        // Called when the menu item labeled "Save" is selected.
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (m_packageFilePath == null)
                SavePackageFile();
            else
                SavePackageFile(m_packageFilePath);
        }

        // Called when the menu item labeled "Save As..." is selected.
        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SavePackageFile();
        }

        // Called when the menu item labeled "Exit" is selected.
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        // Called when the user drags an item over the form.
        private void PdcTestBench_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }

        // Called when the user drops an item into the form.
        private void PdcTestBench_DragDrop(object sender, DragEventArgs e)
        {
            string[] fileNames = e.Data.GetData(DataFormats.FileDrop) as string[];

            if (fileNames != null)
            {
                string filePath = fileNames[0];

                if (filePath.EndsWith(".tbp"))
                    OpenPackageFile(filePath);
            }
        }

        // Called when the input data file is changed.
        private void generatorInputDataFileTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the input data file text box loses focus.
        private void generatorInputDataFileTextBox_Leave(object sender, EventArgs e)
        {
            if(m_inputFileChanged)
                CleanSampleFile();
        }

        // Called when the configuration frame file is changed.
        private void generatorConfigurationFrameFileTextBox_TextChanged(object sender, EventArgs e)
        {
            m_inputFileChanged = true;
            UnsavedChanges = true;
        }

        // Called when the generator's button labeled "Browse..." is selected.
        private void generatorBrowseButton_Click(object sender, EventArgs e)
        {
            DialogResult result = inputDataFileOpenFileDialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                generatorInputDataFileTextBox.Text = inputDataFileOpenFileDialog.FileName;
                CleanSampleFile();
            }
        }

        // Called when the browse button for the configuration frame file is clicked.
        private void generatorConfigurationFrameFileBrowseButton_Click(object sender, EventArgs e)
        {
            DialogResult result = configurationFrameOpenFileDialog.ShowDialog();

            if (result == DialogResult.OK)
                generatorConfigurationFrameFileTextBox.Text = configurationFrameOpenFileDialog.FileName;
        }

        // Called when the generator frame rate is changed.
        private void generatorFrameRateTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the user changes the generator's output protocol.
        private void generatorTcpRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the user changes the generator's output protocol.
        private void generatorUdpRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the user changes the generator's access ID.
        private void generatorAccessIdTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the number of generators has changed.
        private void pmuCountNumericUpDown_ValueChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
            UpdateGeneratorEndPort();
        }

        // Called when the starting port of the generator port range has changed.
        private void generatorStartPortTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
            UpdateGeneratorEndPort();
        }

        // Updates the end port of the generator port range.
        private void UpdateGeneratorEndPort()
        {
            int endPort = (int)pmuCountNumericUpDown.Value + int.Parse(generatorStartPortTextBox.Text) - 1;
            generatorEndPortLabel.Text = "- " + endPort;
        }

        // Called when the make of the concentrator is changed.
        private void concentratorMakeTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the model of the concentrator is changed.
        private void concentratorModelTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the firmware of the concentrator is changed.
        private void concentratorFirmwareTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the host name of the concentrator is changed.
        private void concentratorHostNameTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the access ID of the concentrator is changed.
        private void receiverAccessIdTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the user chooses the transport protocol to be used by the receiver.
        private void receiverTcpRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the user chooses the transport protocol to be used by the receiver.
        private void receiverUdpRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
            receiverCommandChannelPortLabel.Visible = receiverUdpRadioButton.Checked;
            receiverCommandChannelPortTextBox.Visible = receiverUdpRadioButton.Checked;
        }

        // Called when the receiver's port number is changed.
        private void receiverPortTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the output file is changed.
        private void receiverOutputFileTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the timestamp file is changed.
        private void receiverTimestampFileTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the receiver's browse button is clicked.
        private void receiverOutputFileBrowseButton_Click(object sender, EventArgs e)
        {
            DialogResult result = outputDataFileSaveFileDialog.ShowDialog();

            if (result == DialogResult.OK)
            {
                string outputFilePath = outputDataFileSaveFileDialog.FileName;
                receiverOutputFileTextBox.Text = outputFilePath;

                if (string.IsNullOrEmpty(receiverTimestampFileTextBox.Text))
                    receiverTimestampFileTextBox.Text = outputFilePath.Substring(0, outputFilePath.LastIndexOf('.')) + ".rft";
            }
        }

        // Called when the browse button for the timestamp file is clicked.
        private void receiverTimestampFileBrowseButton_Click(object sender, EventArgs e)
        {
            DialogResult result = timestampFileSaveFileDialog.ShowDialog();

            if (result == DialogResult.OK)
                receiverTimestampFileTextBox.Text = timestampFileSaveFileDialog.FileName;
        }

        // Called when the command channel's port is changed.
        private void receiverCommandChannelPortTextBox_TextChanged(object sender, EventArgs e)
        {
            UnsavedChanges = true;
        }

        // Called when the button labeled "Start" or "Stop" is selected.
        private void startStopButton_Click(object sender, EventArgs e)
        {
            if (startStopButton.Text == "Stop")
                m_testBench.Stop();
            else
            {
                if (!m_unsavedChanges && m_packageFilePath != null)
                {
                    m_testBench.Initialize(m_packageFile);
                    m_testBench.Start();
                }
                else
                {
                    SaveChangesForm dialog = new SaveChangesForm(packageSaveFileDialog);
                    DialogResult result = dialog.ShowDialog();

                    if (result == DialogResult.OK)
                    {
                        if (dialog.FilePath != null)
                            SavePackageFile(dialog.FilePath);
                        else if (m_packageFilePath != null)
                            SavePackageFile(m_packageFilePath);
                        else
                            SavePackageFile();

                        m_testBench.Initialize(m_packageFile);
                        m_testBench.Start();
                    }
                }
            }
        }

        // Called when the test bench needs to display an error message to the user without failing.
        private void m_testBench_DisplayErrorMessage(object sender, EventArgs<string> e)
        {
            MessageBox.Show(e.Argument);
        }

        // Called when the test bench is successfully started.
        private void m_testBench_TestBenchStarted(object sender, EventArgs e)
        {
            startStopButton.Text = "Stop";

            foreach (TabPage page in tabControl.TabPages)
                page.Enabled = false;
        }

        // Called when the test bench is successfully stopped.
        private void m_testBench_TestBenchStopped(object sender, EventArgs e)
        {
            startStopButton.Text = "Start";

            foreach (TabPage page in tabControl.TabPages)
                page.Enabled = true;
        }

        #endregion

    }
}
